package de.school.cbs.cube;
import javax.bluetooth.*;
import javax.microedition.io.*;

import java.util.logging.ConsoleHandler;
import java.util.logging.FileHandler;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.logging.SimpleFormatter;
import java.io.*;

public class JLedServer {
    private final UUID uuid = new UUID("0000110100001000800000805F9B34FB", false);
    private final String sdpname = "BluetoothCube";
    private StreamConnectionNotifier scn;
    private final Logger log = Logger.getLogger(JLedServer.class.getName());
    private StreamConnection connection;
    private RemoteDevice dev;

    public JLedServer() {
    	// Set logging level
    	Handler handler = new ConsoleHandler();
    	/*handler.setLevel( Level.INFO );
    	this.log.addHandler( handler );*/
    	try {
			handler = new FileHandler("/tmp/server.log", 20971520, 5, true);
			handler.setLevel(Level.FINEST);
			handler.setFormatter(new SimpleFormatter());
			this.log.addHandler(handler);
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
    	this.log.setLevel( Level.ALL );
    	
        this.log.info("Open bluetooth server...");
        try {
            this.scn = (StreamConnectionNotifier)Connector.open("btspp://localhost:" + this.uuid + ";name=" + this.sdpname);
        } catch (IOException ioe) {
            this.log.severe("Could not open connection");
            System.exit(255);
            // Do nothing !?
        }

        do {
        	this.accept();
        	this.start();
        	this.log.info("Connection to device closed.");
        } while(true);
    }

    public void accept() {
        // Wait for client server
        this.log.info("Waiting for client to connect on localhost ...");

        try {
            this.connection = this.scn.acceptAndOpen();
        } catch (IOException ioe) {
            // Do Nothing
        }

        // Remote Device found
        this.log.info("Connection established. Fetching remote device.");
        try {
            this.dev = RemoteDevice.getRemoteDevice(connection); 
            this.log.info("Connected with " + dev.getFriendlyName(true) + " on " + dev.getBluetoothAddress());
        } catch (IOException ioe) {
            this.log.severe("Could not get remote device information.");
        }

    }

    public void start() {
        BufferedReader bReader;
        BufferedWriter bWriter;
        try {
            InputStream inStream = this.connection.openInputStream();
            bReader = new BufferedReader(new InputStreamReader(inStream));
            OutputStream outStream = this.connection.openOutputStream();
            bWriter = new BufferedWriter(new OutputStreamWriter(outStream));

            // Create cube
            LedCube lc = new LedCube();

            // Width / Depth / Height
            bWriter.write(String.format("%c%c%c%n", lc.getWidth(), lc.getDepth(), lc.getHeight()));
            bWriter.flush();

            char data;
            int dataLayer;
            int layer = 0;
            int row = 0;
            int column = 0;
            boolean run = true;

            while (run) {
                try {
                    data = (char) bReader.read();
                    layer = -1;
                    row = -1;
                    column = -1; 
                    if (Character.toLowerCase(data) == 'l') {
                        do {
                            dataLayer = (char) bReader.read();
                            if (dataLayer != ',') {
                                if (layer < 0) {
                                    layer = 0;
                                }
                                layer += (int) dataLayer;
                            }
                        } while (dataLayer != ',');

                        this.log.fine(
                        		"Data: " + String.valueOf(data) + 
                        		" Layer: " + String.valueOf(layer)
                        		);
                        do {
                            dataLayer = (char) bReader.read();
                            if (dataLayer == '0') {
                                lc.changeEnable(layer, row, column, false);
                            } else if (dataLayer == '1') {
                                lc.changeEnable(layer, row, column, true);
                            }
                        } while (dataLayer != '0' && dataLayer != '1');
                    } else if (Character.toLowerCase(data) == 's') {
                        layer  = bReader.read(); // Y (x)
                        row    = bReader.read(); // X (y)
                        column = bReader.read(); // Z (z)
                        dataLayer = (int) bReader.read(); // LED on or off
                        this.log.fine(
                        		"Data: " + String.valueOf(data) + 
                        		" Layer: " + String.valueOf(layer) + 
                        		" Row: " + String.valueOf(row) + 
                        		" Column: " + String.valueOf(column) + 
                        		" On/Off: " + String.valueOf(dataLayer)
                        		);
                        if (dataLayer == 0) {
                            lc.changeEnable(layer, row, column, false);
                        } else if (dataLayer == 1) {
                            lc.changeEnable(layer, row, column, true);
                        }
                    } else if (data == '\n') {
                    	this.log.finest("Got newline");
                    } else if (((int)data) == 65535) {
                    	run = false;
                    } else {
                    	this.log.fine(
                        		"Unknown Data: " + ((int)data)
                    			);
                    }
                } catch (IOException ioe) {
                    this.log.severe("Could not read or write to device.");
                }
            }
        } catch (IOException ioe) {
            this.log.severe("Could not open input / output writing... ");
        }

    }

    public static void main(String[] args) {
        new JLedServer();
    }
}
